package de.eridea.learning.java.usermanagement.logic;

import java.util.Set;

import de.eridea.learning.java.usermanagement.logic.exception.InsufficientDataException;
import de.eridea.learning.java.usermanagement.logic.exception.MalformedDataException;
import de.eridea.learning.java.usermanagement.logic.exception.NameAlreadyExistsException;
import de.eridea.learning.java.usermanagement.logic.exception.NoSuchGroupException;
import de.eridea.learning.java.usermanagement.logic.exception.NoSuchResourceException;
import de.eridea.learning.java.usermanagement.logic.exception.NoSuchUserException;
import de.eridea.learning.java.usermanagement.logic.exception.NothingToDoException;
import de.eridea.learning.java.usermanagement.logic.exception.VerificationFailedException;
import de.eridea.learning.java.usermanagement.logic.to.GroupData;
import de.eridea.learning.java.usermanagement.logic.to.GroupDataEditable;
import de.eridea.learning.java.usermanagement.logic.to.GroupDataLinked;
import de.eridea.learning.java.usermanagement.logic.to.ResourceData;
import de.eridea.learning.java.usermanagement.logic.to.ResourceDataEditable;
import de.eridea.learning.java.usermanagement.logic.to.ResourceDataLinked;
import de.eridea.learning.java.usermanagement.logic.to.UserData;
import de.eridea.learning.java.usermanagement.logic.to.UserDataEditable;
import de.eridea.learning.java.usermanagement.logic.to.UserDataLinked;

/**
 * User management.
 * 
 * @author Frank Bruder
 */
public interface UserService {
	/**
	 * Verify a user�s identity and log in.
	 * 
	 * @param loginName
	 *            The user�s login name.
	 * @param password
	 *            The user�s password
	 * @return a <code>UserDataLinked</code> object representing the verified
	 *         user.
	 * @throws VerificationFailedException
	 *             if a user with the specified login name exists, but the
	 *             password is not correct.
	 * @throws NoSuchUserException
	 *             if the specified login name does not match any registered
	 *             user.
	 */
	public UserDataLinked login(String loginName, String password)
			throws VerificationFailedException, NoSuchUserException;

	/**
	 * Retrieve a user data object.
	 * 
	 * @param loginName
	 * @return a <code>UserDataLinked</code> object representing the user
	 *         registered under the specified login name.
	 * @throws NoSuchUserException
	 *             if no matching login name is registered with the system.
	 */
	public UserDataLinked getUser(String loginName) throws NoSuchUserException;

	/**
	 * Create a new <code>UserDataEditable</code> instance which can be
	 * populated with data and used as an argument to the method
	 * <code>registerUser()</code>.
	 */
	public UserDataEditable newUser(String loginName);

	/**
	 * Register a new user account.
	 * 
	 * @param userData
	 *            a data transfer object for the new account�s user data.
	 * @param password
	 *            the new user�s password.
	 * @return a <code>UserDataLinked</code> object representing the newly
	 *         registered user.
	 * @throws InsufficientDataException
	 *             if a property of the <code>userData</code> parameter which is
	 *             required for registration (e.g. the login name) was not set.
	 * @throws MalformedDataException
	 *             if the properties of the <code>userData</code> parameter
	 *             violate any of the imposed constraints. (E.g. the login name
	 *             contains a line feed character, and this is not allowed.)
	 * @throws NameAlreadyExistsException
	 *             if the requested login name can not be used because it
	 *             matches an already registered name.
	 */
	public UserDataLinked registerUser(UserDataEditable userData,
			String password) throws InsufficientDataException,
			MalformedDataException, NameAlreadyExistsException;

	/**
	 * Retrieve a user�s data as an editable object.
	 * 
	 * @param loginName
	 *            The login name to look up.
	 * @return A <code>UserDataEditable</code> object, which can be edited and
	 *         then submitted to the method <code>updateUser()</code>.
	 * @throws NoSuchUserException
	 *             if no matching login name is registered with the system.
	 */
	public UserDataEditable getEditableUser(String loginName)
			throws NoSuchUserException;

	/**
	 * Save changes made on the provided <code>UserDataEditable</code> object.
	 * 
	 * @param user
	 *            a <code>UserDataEditable</code> object.
	 * @throws InsufficientDataException
	 *             when attempting to set a required property to
	 *             <code>null</code>.
	 * @throws MalformedDataException
	 *             when attempting to save a state which violates constraints
	 *             imposed on user data or to change a property which is
	 *             required to be constant.
	 * @throws IllegalArgumentException
	 *             if the provided <code>UserDataEditable</code> object does not
	 *             represent a registered user or was not created by the same
	 *             <code>UserService</code>.
	 * @throws NoSuchUserException
	 *             if the login name does not match that of any registered user.
	 * @throws NameAlreadyExistsException
	 *             when attempting to set a string field which is required to be
	 *             unique (i.e. login name or display name) to a value which is
	 *             already in use.
	 */
	public void updateUser(UserDataEditable user)
			throws InsufficientDataException, MalformedDataException,
			IllegalArgumentException, NoSuchUserException,
			NameAlreadyExistsException;

	/**
	 * Change a user�s password.
	 * 
	 * @param loginName
	 * @param newPassword
	 * @throws NoSuchUserException
	 *             if the provided login name does not match any registered
	 *             user.
	 */
	public void updateUserPassword(String loginName, String newPassword)
			throws NoSuchUserException;

	/**
	 * Retrieve a group data object.
	 * 
	 * @param groupName
	 * @return a <code>GroupDataLinked</code> object representing the group of
	 *         the specified name.
	 * @throws NoSuchGroupException
	 *             if no matching group name is registered with the system.
	 */
	public GroupDataLinked getGroup(String groupName)
			throws NoSuchGroupException;

	/**
	 * Create a new <code>GroupDataEditable</code> instance which can be
	 * populated with data and used as an argument to the method
	 * <code>registerGroup()</code>.
	 * 
	 * @param groupName
	 *            The name of the new group.
	 * @return a new editable group data object.
	 */
	public GroupDataEditable newGroup(String groupName);

	/**
	 * Register a new group.
	 * 
	 * @param groupData
	 *            a data transfer object for the new group�s data.
	 * @return a <code>GroupDataLinked</code> object representing the newly
	 *         registered group.
	 * @throws InsufficientDataException
	 *             if a property of the <code>groupData</code> parameter which
	 *             is required for registration (e.g. the group owner) was not
	 *             set.
	 * @throws MalformedDataException
	 *             if the properties of the <code>groupData</code> parameter
	 *             violate any of the imposed constraints. (E.g. the group name
	 *             contains a line feed character, and this is not allowed.)
	 * @throws NameAlreadyExistsException
	 *             if the requested group name can not be used because it
	 *             matches an already registered name.
	 */
	public GroupDataLinked registerGroup(GroupDataEditable groupData)
			throws InsufficientDataException, MalformedDataException,
			NameAlreadyExistsException;

	/**
	 * Retrieve a group�s data as an editable object.
	 * 
	 * @param groupName
	 *            the group name to look up
	 * @return A <code>UserDataEditable</code> object, which can be edited and
	 *         then submitted to the method <code>updateGroup()</code>.
	 * @throws NoSuchGroupException
	 *             if no matching group name is registered with the system.
	 */
	public GroupDataEditable getEditableGroup(String groupName)
			throws NoSuchGroupException;

	/**
	 * Save changes made on the provided <code>GroupDataEditable</code> object.
	 * 
	 * @param group
	 *            a <code>GroupDataEditable</code> object.
	 * @throws InsufficientDataException
	 *             when attempting to set a required property to
	 *             <code>null</code>.
	 * @throws MalformedDataException
	 *             when attempting to save a state which violates constraints
	 *             imposed on group data or to change a property which is
	 *             required to be constant.
	 * @throws IllegalArgumentException
	 *             if the provided <code>GroupData</code> object does not
	 *             represent a registered group or was not created by the same
	 *             <code>UserService</code>.
	 */
	public void updateGroup(GroupDataEditable group)
			throws InsufficientDataException, MalformedDataException,
			IllegalArgumentException, NoSuchGroupException;

	/**
	 * Retrieve the users who are in a group.
	 * 
	 * @param groupName
	 *            The name of a registered group.
	 * @return the set of users who are members of the queried group.
	 */
	public Set<UserData> getUsersInGroup(String groupName)
			throws NoSuchGroupException;

	/**
	 * Retrieve all registered groups
	 */
	public Set<GroupData> getGroups();
	
	/**
	 * Retrieve a resource data object.
	 * 
	 * @param resourceIdentifier
	 *            The unique string identifier of the resource.
	 * @return a <code>ResourceDataLinked</code> object representing the
	 *         resource.
	 * @throws NoSuchResourceException
	 *             if no matching resource identifier is registered with the
	 *             system.
	 */
	public ResourceDataLinked getResource(String resourceIdentifier)
			throws NoSuchResourceException;

	/**
	 * Create a new <code>ResourceDataEditable</code> instance which can be
	 * populated with data and used as an argument to the method
	 * <code>registerResource()</code>.
	 * 
	 * @param resourceIdentifier
	 *            The string identifier of the new resource.
	 * @return an editable resource data object.
	 */
	public ResourceDataEditable newResource(String resourceIdentifier);

	/**
	 * Register a new resource.
	 * 
	 * @param resourceData
	 *            a data transfer object for the new resource�s data.
	 * @return a <code>ResourceDataLinked</code> object representing the newly
	 *         registered resource.
	 * @throws InsufficientDataException
	 *             if a property of the <code>resourceData</code> parameter
	 *             which is required for registration (e.g. the resource
	 *             identifier) was not set.
	 * @throws MalformedDataException
	 *             if the properties of the <code>resourceData</code> parameter
	 *             violate any of the imposed constraints. (E.g. the resource
	 *             identifier does not adhere to the URI syntax, and it is
	 *             required to.)
	 * @throws NameAlreadyExistsException
	 *             if the requested resource identifier can not be used because
	 *             it matches an already registered string identifier.
	 */
	public ResourceDataLinked registerResource(ResourceDataEditable resourceData)
			throws InsufficientDataException, MalformedDataException,
			NameAlreadyExistsException;

	/**
	 * Retrieve a resource�s data as an editable object.
	 * 
	 * @param resourceIdentifier
	 *            The unique string identifier of the resource.
	 * @return a <code>ResourceDataEditable</code> object,which can be edited
	 *         and then submitted to the method <code>updateResource()</code>.
	 * @throws NoSuchResourceException
	 *             if no matching resource identifier is registered with the
	 *             system.
	 */
	public ResourceDataEditable getEditableResource(String resourceIdentifier)
			throws NoSuchResourceException;

	/**
	 * Save changes made on the provided <code>ResourceDataEditable</code>
	 * object.
	 * 
	 * @param resource
	 *            a <code>ResourceDataEditable</code> object representing a
	 *            registered resource.
	 * @throws InsufficientDataException
	 *             when attempting to set a required property to
	 *             <code>null</code>.
	 * @throws MalformedDataException
	 *             when attempting to save a state which violates constraints
	 *             imposed on group data or to change a property which is
	 *             required to be constant.
	 * @throws IllegalArgumentException
	 *             if the provided <code>ResourceDataEditable</code> object does
	 *             not represent a registered resource or was not created by the
	 *             same <code>UserService</code>.
	 */
	public void updateResource(ResourceDataEditable resource)
			throws InsufficientDataException, MalformedDataException,
			IllegalArgumentException, NoSuchResourceException;

	/**
	 * Test whether a user is in a group.
	 * 
	 * @param user
	 * @param group
	 * @return <code>true</code> if the user is in the group, <code>false</code>
	 *         otherwise.
	 */
	public boolean isUserInGroup(String user, String group);

	/**
	 * Add a user to a group.
	 * 
	 * @param user
	 * @param group
	 * @throws NothingToDoException
	 *             if <code>user</code> already is in <code>group</code>.
	 */
	public void addUserToGroup(String user, String group)
			throws NothingToDoException, NoSuchUserException,
			NoSuchGroupException;

	/**
	 * Remove a user from a group.
	 * 
	 * @param user
	 * @param group
	 * @throws NothingToDoException
	 *             if <code>user</code> is not in <code>group</code>.
	 */
	public void removeUserFromGroup(String user, String group)
			throws NothingToDoException, NoSuchUserException,
			NoSuchGroupException;

	/**
	 * Test whether a user has permission to access a resource.
	 * 
	 * Usually a resource may be accessed by its owner and by users who are in
	 * groups to which access rights have been granted. But extensions may hard
	 * code additional rules (such as marking one user as superuser with
	 * universal access rights). So don�t manually check group permissions and
	 * ownership if user permission is what you need to know.
	 * 
	 * @param user
	 *            The login name of the user.
	 * @param resource
	 *            The string identifier of the resource.
	 * @return <code>true</code> if access is permitted, <code>false</code>
	 *         otherwise.
	 */
	public boolean hasPermission(String user, String resource)
			throws NoSuchUserException, NoSuchResourceException;

	/**
	 * Grant a group permission to access a resource.
	 * 
	 * @param group
	 *            The name of the group.
	 * @param resource
	 *            The string identifier of the resource.
	 * @throws NothingToDoException
	 *             If the group already was permitted to access the resource.
	 * @throws NoSuchGroupException
	 *             If the group name does not match any entry in the database.
	 * @throws NoSuchResourceException
	 *             If the resource identifier does not match any entry in the
	 *             database.
	 */
	public void grantPermission(String group, String resource)
			throws NothingToDoException, NoSuchGroupException,
			NoSuchResourceException;

	/**
	 * Revoke a group permission to access a resource.
	 * 
	 * @param group
	 *            The name of the group.
	 * @param resource
	 *            The string identifier of the resource.
	 * @throws NothingToDoException
	 *             If the group had no permission to access the resource before
	 *             the method was invoked.
	 * @throws NoSuchGroupException
	 *             If the group name does not match any entry in the database.
	 * @throws NoSuchResourceException
	 *             If the resource identifier does not match any entry in the
	 *             database.
	 */
	public void revokePermission(String group, String resource)
			throws NothingToDoException, NoSuchGroupException,
			NoSuchResourceException;

}
